// 作用域规则
// RAII的变量不只是在栈中保存数据，也占用资源，比如说Box<T>占用堆中的内存
// RAII(资源获取即初始化)，在任何对象在离开作用域时，它的析构函数就会被占用，然后占有的资源就被释放（避免内存泄露）

fn main(){
    // 在堆上分配一个整型数据
        let _box2 = Box::new(5i32);

    // 嵌套作用域：
        {
    // 在堆上分配一个整型数据
            let _box3 = Box::new(4i32);

    // `_box3` 在这里被销毁，内存得到释放
        }
    // 当然我们可以使用 valgrind 对内存错误进行仔细检查：

    //析构函数
    // rust中的析构函数是通过Drop trait提供的，当资源离开作用域，就调用析构函数
    struct ToDrop;

    // 只要为那些需要自己的析构函数逻辑的类型实现就可以了
    impl Drop for ToDrop {
        fn drop(&mut self) {
            println!("ToDrop is being dropped");
        }
    }

    // 所有权和移动
    // 因为变量要负责释放它们拥有的资源，所以资源只能拥有一个所有者（防止资源的重复释放）
    // 注意并非所有变量都拥有资源（引用）

    // 在进行赋值(let x=y),或通用值来传递函数参数（foo(x)）的时候，资源的所有权会发生转移（资源的移动）
    // 在移动资源之后，原来的所有者不能再被使用，（避免悬挂指针的产生）


    // 浅拷贝（只拷贝栈上的内容）
    let s = "Hello world!".to_string();
    let s1 = s;

    // 深拷贝（栈上的内容外，换拷贝堆内存中的内容）
    // 当需要拷贝堆上的数据时，可以使用clone方法
    let s2 = s.clone(); // 这将发生深拷贝
    // 不是所有的类型都能使用clone方法进行深拷贝，只有实现了Clone trait类型才能调用该方法

    //Copy
    // 在任意时刻，值有且仅有一个所有者
    // let b=a发生时，就将变量a拥有的值移到了b，此时a应该回到未初始状态，但实际上不一定
    //  不一定的原因是，部分类型实现了Copy trait，在值移动时会对值进行自动拷贝，能让变量a仍拥有原来的值
    //  默认实现了Copy trait的类型有：
        //  所有整数类型，比如u32
        //  所有的浮点数类型，比如f64
        //  布尔类型
        //  字符类型（char）
        //  元祖（当且仅当其包含的类型也都是Copy的时候。比如(i32, i32)是Copy的，但(i32, String)不是；）
        //  数组（当且仅当其包含的类型也都是Copy的时候。比如[i32; 5]是Copy的，但[String; 5]不是；）
        // 共享指针类型或共享引用类型


    // 将值传给函数
    // fn main() {
    //     let s = String::from("hello");
    //     takes_ownership(s);
    //     // println!("s: {:?}", s); // 打开编译会报错，因为s的所有权在上一行已经转移到take_ownership函数中了
    //
    //     let x = 5;
    //     makes_copy(x);
    //     println!("x: {:?}", x); // 不会报错，因为上一行将x传到makes_copy函数时会自动拷贝x的值到函数中
    // }
    //
    // fn takes_ownership(some_string: String) {
    //     println!("{}", some_string);
    // }
    //
    // fn makes_copy(some_integer: i32) {
    //     println!("{}", some_integer);
    // }

    // 返回值和作用域
    // fn main() {
    //     let s1 = gives_ownership();        // gives_ownership 将返回值转移给 s1
    //     let s2 = String::from("hello");    // s2 进入作用域
    //     let s3 = takes_and_gives_back(s2); // s2 被移动到 takes_and_gives_back 中，
    //     // 它也将返回值移给 s3
    // } // 这里，s3 移出作用域并被丢弃。s2 也移出作用域，但已被移走，所以什么也不会发生。s1 离开作用域并被丢弃
    //
    // fn gives_ownership() -> String {             // gives_ownership 会将返回值移动给调用它的函数
    //     let some_string = String::from("yours"); // some_string 进入作用域。
    //     some_string                              // 返回 some_string 并移出给调用的函数
    // }
    //
    // // takes_and_gives_back 将传入字符串并返回该值
    // fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域
    //     a_string  // 返回 a_string 并移出给调用的函数
    // }


    // 转移所有权的方式（引用）
    // 引用：本质上是一个指针，它存储一个地址，通过它可以访问存在在该地址上其他变量的数据
    //      与指针不同的是，引用确保指向某个特性类型的有效值，对于一个变量的引用（在变量前加&）
    //      引用只能使用变量，并不允许改变变量的值，如果需要改变变量的值，需要使用可变引用
    //      与引用相对的就是 解引用（*）
    //  引用的规则：
    //         在任意给定时间，要么只能有一个可变引用，那么只能有多个不可变引用
    //         引用必须总是有效的（不能是悬垂引用）

    //  借用：获取变量的引用
    //       通过借用，允许使用被引用变量绑定的值，同时又没有移动该变量的所有权

    // fn main() {
    //     let s2 = String::from("hello");
    //     let s3 = &s2;   //s3是对s2的借用，s3并不拥有String::from("hello")的所有权，s2的所有权没有改变
    //     print(s3);      //在函数中使用s3
    //     println!("s2 is {:?}", s2);   //仍然可以使用s2
    // }
    //
    // fn print(s: &String) {
    //     println!("s is: {:?}", s);
    // }

    // 可变引用
    // fn main() {
    //     let mut s = String::from("hello");
    //     change(&mut s);
    // }
    //
    // fn change(some_string: &mut String) {
    //     some_string.push_str(", world");    // 可变引用，可以对变量进行修改
    // }

    // 引用的作用域（新版）
    // {
    //     ...
    //     let s = "Hello".to_string();
    //     let r = &s;                      // r的作用域开始位置
    //     println!("r = {:?}", r);         // r的作用域结束位置
    //     ...            //后面不再使用 r
    // }


    // 数据竞争：可由三个行为造成：
    //     1. 两个或更多指针同时访问同一数据
    //     2. 至少有一个指针被用来写入数据
    //     3. 没有同步数据访问的机制

    //  使用可变引用的限制
    //  1. 同一作用域，特定数据只能有一个可变引用
    // fn main() {
    //     let mut s1 = String::from("hello");
    //     let r1 = &mut s1; // 可变引用
    //     let r2 = &mut s1; // 错误，同一作用域变量只允许被进行一次可变借用
    //     println!("{}, {}", r1, r2);
    // }

    //  2. 同一作用域，可变引用和不可变引用不能同时存在
    // fn main() {
    //     let mut s = String::from("hello");
    //
    //     let r1 = &s; // 没问题
    //     let r2 = &s; // 没问题
    //     println!("{} and {}", r1, r2);
    //     // 此位置之后 r1 和 r2 不再使用，  新编译器中： r1和r2离开了其作用域
    //
    //     let r3 = &mut s; // 没问题，因为r1和r2已不存在，没有同时存在对s的引用和可变引用
    //     println!("{}", r3);
    // }       // 老编译器中： r1、r2、r3的作用域都是在花括号之前结束


    // 悬垂引用（悬垂指针）
    // 在具有指针的语言中（如C/C++），很容易通过释放内存但是保留指向它的指针而错误的生成一个悬垂指针
    // 使用了已经释放的内存

    // 错误
    // fn main() {
    //     let reference_to_nothing = dangle();
    // }
    // fn dangle() -> &String {
    //     let s = String::from("hello");
    //     &s    // s在花括号前离开作用域，将会变得无效，返回的指向s的引用将是一个悬垂引用
    // }

    // 正确
    // fn main() {
    //     let s = no_dangle();
    //     println!("s = {:?}", s);
    // }
    //
    // fn no_dangle() -> String {
    //     let s = String::from("hello");
    //     s
    // }    // 此处s虽然离开了函数这个作用域范围，但是它的所有权是被转移出去了，值并没有释放


    // 此函数取得堆分配的内存的所有权
    fn destroy_box(c: Box<i32>) {
        println!("Destroying a box that contains {}", c);

        // `c` 被销毁且内存得到释放
    }

    // 栈分配的整型
    let x = 5u32;

    // 将 `x` *复制*到 `y`——不存在资源移动
    let y = x;

    // 两个值各自都可以使用
    println!("x is {}, and y is {}", x, y);

    // `a` 是一个指向堆分配的整数的指针
    let a = Box::new(5i32);

    println!("a contains: {}", a);

    // *移动* `a` 到 `b`
    let b = a;
    // 把 `a` 的指针地址（而非数据）复制到 `b`。现在两者都指向
    // 同一个堆分配的数据，但是现在是 `b` 拥有它。

    // 报错！`a` 不能访问数据，因为它不再拥有那部分堆上的内存。
    println!("a contains: {}", a);
    // 试一试 ^ 去掉此行注释

    // 此函数从 `b` 中取得堆分配的内存的所有权
    destroy_box(b);

    // 此时堆内存已经被释放，这个操作会导致解引用已释放的内存，而这是编译器禁止的。
    // 报错！和前面出错的原因一样。
    println!("b contains: {}", b);
    // 试一试 ^ 去掉此行注释

    // 当所有权转移时，数据的可变性可能发生改变

    let immutable_box = Box::new(5u32);

    println!("immutable_box contains {}", immutable_box);

    // 可变性错误
    //*immutable_box = 4;

    // *移动* box，改变所有权（和可变性）
    let mut mutable_box = immutable_box;

    println!("mutable_box contains {}", mutable_box);

    // 修改 box 的内容
    *mutable_box = 4;

    println!("mutable_box now contains {}", mutable_box);

    // 借用
    // 希望访问数据，同时不取得其所有权（借用）
    // 对象可以通用引用（&T）来传递，从而取代通过值(T)来传递
    // 编译器（通过借用检查）静态地保证了引用总是指向有效的对象。也就是说，当存在引用指向一个对象时，该对象不能被销毁

    // 可变数据可以使用 &mut T 进行可变借用。这叫做可变引用，它使借用者可以读/写数据。相反，
    // &T 通过不可变引用来借用数据，借用者可以读数据而不能更改数据：

    #[allow(dead_code)]
    #[derive(Clone, Copy)]
    struct Book {
        // `&'static str` 是一个对分配在只读内存区的字符串的引用
        author: &'static str,
        title: &'static str,
        year: u32,
    }

    // 此函数接受一个对 Book 类型的引用
    fn borrow_book(book: &Book) {
        println!("I immutably borrowed {} - {} edition", book.title, book.year);
    }

    // 此函数接受一个对可变的 Book 类型的引用，它把年份 `year` 改为 2014 年
    fn new_edition(book: &mut Book) {
        book.year = 2014;
        println!("I mutably borrowed {} - {} edition", book.title, book.year);
    }


    // 创建一个名为 `immutabook` 的不可变的 Book 实例
    let immutabook = Book {
        // 字符串字面量拥有 `&'static str` 类型
        author: "Douglas Hofstadter",
        title: "Gödel, Escher, Bach",
        year: 1979,
    };

    // 创建一个 `immutabook` 的可变拷贝，命名为 `mutabook`
    let mut mutabook = immutabook;

    // 不可变地借用一个不可变对象
    borrow_book(&immutabook);

    // 不可变地借用一个可变对象
    borrow_book(&mutabook);

    // 可变地借用一个可变对象
    new_edition(&mut mutabook);


    // 别名使用
    // 数据可以多次不可变借用，但是在不可变借用的同时，原始数据不能使用可变借用
    // 同一时间内只允许一次可变借用。仅当最后一次使用可变引用之后，原始数据才可以再次借用

    // ref模式（ref可用来创建结构体/元组的字段的引用）

    #[derive(Clone, Copy)]
    struct Point { x: i32, y: i32 }

    // 赋值语句中左边的 `ref` 关键字等价于右边的 `&` 符号。
    let ref ref_c1 = c;
    let ref_c2 = &c;

    println!("ref_c1 equals ref_c2: {}", *ref_c1 == *ref_c2);

    let point = Point { x: 0, y: 0 };

    // 在解构一个结构体时 `ref` 同样有效。
    let _copy_of_x = {
        // `ref_to_x` 是一个指向 `point` 的 `x` 字段的引用。
        let Point { x: ref ref_to_x, y: _ } = point;

        // 返回一个 `point` 的 `x` 字段的拷贝。
        *ref_to_x
    };

    // `point` 的可变拷贝
    let mut mutable_point = point;

    {
        // `ref` 可以与 `mut` 结合以创建可变引用。
        let Point { x: _, y: ref mut mut_ref_to_y } = mutable_point;

        // 通过可变引用来改变 `mutable_point` 的字段 `y`。
        *mut_ref_to_y = 1;
    }

    println!("point is ({}, {})", point.x, point.y);
    println!("mutable_point is ({}, {})", mutable_point.x, mutable_point.y);

    // 包含一个指针的可变元组
    let mut mutable_tuple = (Box::new(5u32), 3u32);

    {
        // 解构 `mutable_tuple` 来改变 `last` 的值。
        let (_, ref mut last) = mutable_tuple;
        *last = 2u32;
    }

    println!("tuple is {:?}", mutable_tuple);

    // 生命周期
    // 编译器（中的借用检查器）用它来保证所有的借用都是有效的
    // 一个变量的生命周期在它创建的时候开始，在它销毁的时候结束

    // 作用域和生命周期是不同的
    // 我们通过 & 来借用一个变量。该借用拥有一个生命周期，此生命周期由它声明的位置决定。
    // 于是，只要该借用在出借者（lender）被销毁前结束，借用就是有效的。然而，借用的作用域则是由使用引用的位置决定的

    // 显式标注
    // 借用检查器使用显式的生命周期标记来明确引用的有效时间持续多久，可以用' 撇号显式标出生命周期

    // foo<'a>
    // `foo` 带有一个生命周期参数 `'a`

    // foo<'a, 'b>
    // `foo` 带有生命周期参数 `'a` 和 `'b`
    //     在上面这种情形中，foo 的生命周期不能超出 'a 和 'b 中任一个的周期。

    // `print_refs` 接受两个 `i32` 的引用，它们有不同的生命周期 `'a` 和 `'b`。
    // 这两个生命周期都必须至少要和 `print_refs` 函数一样长。
    fn print_refs<'a, 'b>(x: &'a i32, y: &'b i32) {
        println!("x is {} and y is {}", x, y);
    }

    // 不带参数的函数，不过有一个生命周期参数 `'a`。
    fn failed_borrow<'a>() {
        let _x = 12;

        // 报错：`_x` 的生命周期不够长
        //let y: &'a i32 = &_x;
        // 在函数内部使用生命周期 `'a` 作为显式类型标注将导致失败，因为 `&_x` 的
        // 生命周期比 `y` 的短。短生命周期不能强制转换成长生命周期。
    }

    // 创建变量，稍后用于借用。
    let (four, nine) = (4, 9);

    // 两个变量的借用（`&`）都传进函数。
    print_refs(&four, &nine);
    // 任何被借用的输入量都必须比借用者生存得更长。
    // 也就是说，`four` 和 `nine` 的生命周期都必须比 `print_refs` 的长。


}




